package com.tdk.common.executor

import android.util.Log
import com.tdk.common.util.CollectionUtils
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit

class ThreadPoolScheduledExecutor : ScheduledExecutor {
    private val executorService: ScheduledExecutorService
    private val futures: ConcurrentLinkedQueue<RunnableWithScheduleFuture>

    init {
        executorService = ScheduledThreadPoolExecutor(
            CORE_POOL_SIZE
        ) { r: Runnable? -> Thread(r, "ThreadPoolScheduledExecutor") }
        futures = ConcurrentLinkedQueue()
    }

    override fun postDelay(r: Runnable?, delay: Long) {
        if (r == null) {
            Log.e(TAG, "postDelay: runnable is null")
            return
        }
        if (CollectionUtils.findItem(futures) { item -> r === item.runnable } != null) {
            Log.e(TAG, "postDelay: runnable already scheduled")
            return
        }
        val schedule = executorService.schedule({
            r.run()
            //remove task from futures when task is terminated
            cancel(r)
        }, delay, TimeUnit.MILLISECONDS)
        futures.add(RunnableWithScheduleFuture(r, schedule))
    }

    override fun postInterval(r: Runnable?, delay: Long, interval: Long) {
        if (r == null) {
            Log.e(TAG, "postInterval: runnable is null")
            return
        }
        if (CollectionUtils.findItem(futures) { item -> r === item.runnable } != null) {
            Log.e(TAG, "postInterval: runnable already scheduled")
            return
        }
        val scheduledFuture = executorService.scheduleWithFixedDelay(r, delay, interval, TimeUnit.MILLISECONDS)
        futures.add(RunnableWithScheduleFuture(r, scheduledFuture))
    }

    override fun postIntervalRange(r: Runnable?, delay: Int, interval: Long, range: Int) {
        postIntervalRange(r, null, delay, interval, range)
    }

    override fun postIntervalRange(r: Runnable?, onTimeout: Runnable?, delay: Int, interval: Long, range: Int) {
        if (r == null) {
            Log.e(TAG, "postIntervalRange: runnable is null")
            return
        }
        if (CollectionUtils.findItem(futures) { item -> r === item.runnable } != null) {
            Log.e(TAG, "postIntervalRange: runnable already scheduled")
            return
        }
        val scheduledFuture = executorService.scheduleWithFixedDelay(object : Runnable {
            var count = 0
            override fun run() {
                count++
                if (count <= range) {
                    Log.i(TAG, "IntervalRange count:$count")
                    r.run()
                } else {
                    onTimeout?.run()
                    cancel(r)
                }
            }
        }, delay.toLong(), interval, TimeUnit.MILLISECONDS)
        futures.add(RunnableWithScheduleFuture(r, scheduledFuture))
    }

    override fun cancel(future: ScheduledFuture<*>?) {
        if (future == null || future.isDone || future.isCancelled) {
            Log.i(TAG, "ThreadPoolScheduledExecutor.cancel: future already canceled")
            return
        }
        future.cancel(true)
        val runnableWithScheduleFuture: RunnableWithScheduleFuture? =
            CollectionUtils.findItem(futures) { item -> item.scheduledFuture === future }
        if (runnableWithScheduleFuture != null) {
            futures.remove(runnableWithScheduleFuture)
            Log.i(
                TAG, "ThreadPoolScheduledExecutor.cancel: remove from futures success," +
                        "now task count:" + futures.size
            )
        }
        Log.i(TAG, "ThreadPoolScheduledExecutor.cancel: cancel success")
    }

    override fun cancel(runnable: Runnable?) {
        if (runnable == null) {
            Log.i(TAG, "ThreadPoolScheduledExecutor.cancel: runnable already canceled")
            return
        }
        val runnableWithScheduleFuture: RunnableWithScheduleFuture? = CollectionUtils.findItem(
            futures
        ) { item -> item.runnable === runnable }
        if (runnableWithScheduleFuture != null) {
            cancel(runnableWithScheduleFuture.scheduledFuture)
        }
    }

    override fun cancelAll() {
        if (executorService.isShutdown) {
            Log.i(TAG, "ThreadPoolScheduledExecutor.cancelAll: all future already canceled")
            return
        }
        executorService.shutdownNow()
        Log.i(TAG, "ThreadPoolScheduledExecutor.cancelAll:cancelAll success ")
    }

    private class RunnableWithScheduleFuture internal constructor(
        var runnable: Runnable,
        var scheduledFuture: ScheduledFuture<*>
    )

    companion object {
        private val TAG = ThreadPoolScheduledExecutor::class.java.simpleName
        private val CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 1
    }
}